@file:Suppress("SpellCheckingInspection")

package han.cirno.corrupt.components.school.impl.fafu

import han.cirno.corrupt.Application
import han.cirno.corrupt.R
import han.cirno.corrupt.components.school.SchoolProvider
import han.cirno.corrupt.components.school.class_time.ClassTime
import han.cirno.corrupt.components.school.class_time.SimpleClassTime
import han.cirno.corrupt.components.school.term_time.BasicTermTime
import han.cirno.corrupt.components.school.term_time.TermTime
import han.cirno.corrupt.data.DB
import han.cirno.corrupt.data.SettingKeys
import han.cirno.corrupt.data.instances.UserClassesData
import han.cirno.corrupt.util.ViewUtil
import java.util.Calendar

class FafuSchool: SchoolProvider {
    companion object{
        const val KEY_VIEW_STATE="key_fafu:view_state"
        val supportedInfo= arrayOf(
            SchoolProvider.INFO_CLASSES,
            SchoolProvider.INFO_EXAMS,
            SchoolProvider.INFO_SCORE
        )
        val fafuClassTime= SimpleClassTime.Builder()
            .add(8,0)
            .add(8,50)
            .add(9,55)
            .add(10,45)
            .add(11,35)
            .add(14,0)
            .add(14,50)
            .add(15,50)
            .add(16,40)
            .add(18,25)
            .add(19,15)
            .add(20,5)
            .build()
        val termTime= BasicTermTime()
    }

    override fun getSchoolName(): String = "Fujian Agricultural and Forestry University"
    override fun getClassTime(): ClassTime = fafuClassTime
    override fun getTermTime(): TermTime = termTime
    override fun hasInfo(infoKey: String): Boolean = supportedInfo.contains(infoKey)

    override fun getString(key: String): String? {
        return when(key){
            SchoolProvider.STRING_INFO -> {
                val dataOperation = dataOperation()
                ViewUtil.getString(
                    resId = R.string.card_personalInfo_logged_fafu,
                    paras = arrayOf(
                        getSchoolName(),
                        dataOperation.singleGet(SchoolProvider.KEY_USERNAME,String::class)!!,
                        dataOperation.singleGet(SchoolProvider.KEY_ID,String::class)!!,
                        dataOperation.singleGet(SchoolProvider.KEY_TOKEN,String::class)!!))
            }
            else->null
        }
    }

    override suspend fun performLogin(
        userName: String,
        password: String,
        callback: SchoolProvider.LoginCallback
    ): Result<String> {
        dataOperation()
            .write(SchoolProvider.KEY_ID, userName)
            .write(SchoolProvider.KEY_PASSWORD, password)
            .write(SchoolProvider.KEY_TOKEN,"(Invalid)")
            .save()
        return FafuEduClient.instance.request(
            FafuEduConst.Payload.MainPage,callback)
    }

    override suspend fun fetchInfo(infoKey: String, vararg params: Any?): Result<Any> {
        return try{when(infoKey){
            SchoolProvider.INFO_CLASSES -> fetchClassesTable()
            SchoolProvider.INFO_EXAMS->fetchExamInfo()
            SchoolProvider.INFO_SCORE->fetchScoreInfo()
            else->Result.failure(IllegalArgumentException("Not a valid info key!"))
        }}catch (e:Exception){Result.failure(e)}
    }

    private fun fetchScoreInfo():Result<Unit>{
        FafuEduClient.instance.request(FafuEduConst.Payload.ScorePage())
            .onSuccess {
                val scoreDao=DB.getInstance().scoreDao()
                val scoreList = FafuScoreResolver.analyse(it)
                scoreDao.deleteAll()
                scoreDao.insertAll(scoreList)
            }.onFailure {
                return Result.failure(it)
            }
        return Result.success(Unit)
    }

    private fun fetchExamInfo():Result<Unit>{
        FafuEduClient.instance.request(FafuEduConst.Payload.ExamPage())
            .onSuccess {
                val examDao= DB.getInstance().examDao()
                val examList = FafuExamResolver.analyse(it)
                examDao.deleteAll()
                examDao.insertAll(examList)
            }.onFailure {
                return Result.failure(it)
            }
        return Result.success(Unit)
    }

    private fun fetchClassesTable():Result<Unit>{
        val classEntryDao = DB.getInstance().classEntryDao()
        val classTableDao = DB.getInstance().classTableDao()
        val strange=Application.application.sharedPreferences.getString(SettingKeys.SELECT_DEFAULT_GETTER_METHOD,"0")
        val payload=when (strange) {
            "0" -> {
                FafuEduClient.instance.request(FafuEduConst.Payload.ClassesPage())
                    .onSuccess { dataOperation().write(
                        KEY_VIEW_STATE,
                        FafuEduClient.instance.findViewState(it)) }
                val calendar = Calendar.getInstance()
                val (xnd, xqd) = getTermTime().getTerms(calendar)
                FafuEduClient.instance.request(
                    FafuEduConst.Payload.ClassesPage(
                        dataOperation().singleGet(KEY_VIEW_STATE, String::class), xnd, "", "xnd"
                    )
                ).onSuccess {
                    dataOperation().write(
                        KEY_VIEW_STATE,
                        FafuEduClient.instance.findViewState(it)).save()
                }
                FafuEduConst.Payload.ClassesPage(
                    dataOperation().singleGet(
                        KEY_VIEW_STATE,
                        String::class
                    ), "", xqd, "xqd"
                )
            }
            else -> FafuEduConst.Payload.ClassesPage()
        }
        FafuEduClient.instance.request(payload)
            .onSuccess {
                UserClassesData.makeDirty()
                classEntryDao.deleteAll()
                classTableDao.deleteAll()
                try {
                    FafuClassResolver.analyse(it).run {
                        classTableDao.insert(this)
                        classEntryDao.insertAll(this.classEntries)
                    }
                }catch (e:Exception){
                    e.printStackTrace()
                    return Result.failure(e)
                }
            }.onFailure {
                return Result.failure(it)
            }
        return Result.success(Unit)
    }
}